use super::system_setting;
use crate::{
    config::app_error::AppResult,
    extension::model::config_map::{self, ConfigMap}
    ,
    store::extension_store_client,
};
use once_cell::sync::Lazy;
use serde_json::Value;
use std::{collections::BTreeMap, sync::RwLock};

/// A fetcher that fetches the system configuration from the extension client.
/// If there are ConfigMaps named system-default and system at the same time,
/// the ConfigMap named system will be json merge patch to
/// ConfigMap named system-default
///
///
static CONFIG_MAP_CACHE: Lazy<RwLock<ConfigMap>> = Lazy::new(|| RwLock::new(ConfigMap::default()));

///
pub fn fetch<T: for<'de> serde::Deserialize<'de>>(
    key: &str,
) -> Option<T> {
    match get_values_internal().get(key) {
        None => None,
        Some(string_value) => {
            match serde_json::from_str(string_value) {
                Ok(value) => Some(value),
                Err(e) => None,
            }
        }
    }
}

fn get_values_internal() -> BTreeMap<String, String> {
    get_config_map().data.unwrap_or(BTreeMap::new())
}

/// Get the current ConfigMap from the cache.
pub fn get_config_map() -> ConfigMap {
    CONFIG_MAP_CACHE.read().unwrap().clone()
}

/// Set a new ConfigMap in the cache.
pub fn set_config_map(config_map: ConfigMap) {
    *CONFIG_MAP_CACHE.write().unwrap() = config_map;
}

async fn load_config_map_internal() -> AppResult<Option<ConfigMap>> {
    let system_default = extension_store_client::fetch_or_none::<ConfigMap>(
        config_map::ConfigMap::KIND,
        &system_setting::SYSTEM_CONFIG_DEFAULT.to_string(),
    )
    .await?;

    if system_default.is_none() {
        return Ok(None);
    }

    let system = extension_store_client::fetch_or_none::<ConfigMap>(
        config_map::ConfigMap::KIND,
        &system_setting::SYSTEM_CONFIG.to_string(),
    )
    .await?;
    if system.is_none() {
        return Ok(system_default);
    }
    let system_default = system_default.unwrap();
    let system = system.unwrap();

    let merged_data = merge_data(system_default.data, system.data);

    Ok(Some(ConfigMap::new_config(merged_data)))

    // let merged_config_map = system_default.and_then(|default| {
    //     system.map(|sys| {
    //         let merged_data = merge_data(Some(default.data.clone()), Some(sys.data.clone()));
    //         ConfigMap {
    //             data: merged_data.unwrap_or_default(),
    //             ..default
    //         }
    //     })
    // });

    // Ok(merged_config_map.or(system_default))
}

fn merge_data(
    default_data: Option<BTreeMap<String, String>>,
    data: Option<BTreeMap<String, String>>,
) -> Option<BTreeMap<String, String>> {
    match (default_data, data) {
        (Some(default_data), Some(data)) => {
            let mut copied_default = default_data.clone();
            for (key, value) in data {
                let new_value = if value.is_empty() {
                    copied_default.get(&key).cloned()
                } else {
                    Some(merge_remapping_function(
                        &value,
                        copied_default.get(&key).unwrap_or(&"".to_string()),
                    ))
                };

                match new_value {
                    Some(v) if !v.is_empty() => {
                        copied_default.insert(key, v);
                    }
                    Some(_) => {
                        copied_default.remove(&key);
                    }
                    None => (),
                }
            }
            Some(copied_default)
        }
        (Some(default_data), None) => Some(default_data),
        (None, Some(data)) => Some(data),
        (None, None) => None,
    }
}

fn merge_remapping_function(data_v: &str, default_v: &str) -> String {
    let data_json: Value = serde_json::from_str(data_v).unwrap_or(Value::Null);
    let default_json: Value = serde_json::from_str(default_v).unwrap_or(Value::Null);

    let merged_json = match (data_json, default_json) {
        (Value::Object(data), Value::Object(default)) => {
            let mut merged = default.clone();
            for (k, v) in data {
                merged.insert(k, v);
            }
            Value::Object(merged)
        }
        (Value::Null, default) => default,
        (data, _) => data,
    };

    merged_json.to_string()
}
